﻿using K9Nano.Caching;
using K9Nano.Domain.Entities;
using K9Nano.GlobalOptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace K9Nano.Domain.Caching;

public class UserCacheBase<TUser, TRole, TUserRole, TRolePermission, TDbContext>(IServiceProvider serviceProvider, ITypedCache<UserCacheInfo> cache)
    : IUserCache
    where TDbContext : DbContext
    where TUser : UserBase
    where TRole : RoleBase
    where TUserRole : UserRoleBase<TUser, TRole>
    where TRolePermission : RolePermissionBase<TRole>
{
    public virtual UserCacheInfo? GetUser(long key)
    {
        if (key <= 0) return null;

        var userInfo = cache.Get(key.ToString());
        if (userInfo is null)
        {
            userInfo = GetUserInternal(key);
            if (userInfo != null)
            {
                cache.Add(key.ToString(), userInfo, TimeSpan.FromMinutes(10));
            }
        }
        return userInfo;
    }

    public virtual void Remove(long key) => cache.Remove(key.ToString());

    protected virtual UserCacheInfo? GetUserInternal(long key)
    {
        using var scope = serviceProvider.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<TDbContext>();
        var user = db.Set<TUser>().AsNoTracking().Where(a => a.Id.Equals(key)).FirstOrDefault();
        if (user is null) return null;

        var roles = db.Set<TUserRole>().AsNoTracking().Where(a => a.UserId.Equals(key))
            .Select(a => a.Role)
            .ToList();

        var roleCodes = roles.Select(a => a.Code).ToArray();
        string[] permissions;
        if (!roleCodes.Contains(RoleNames.Admin, StringComparer.Ordinal)
             && roles.Count > 0)
        {
            var roleIds = roles.Select(a => a.Id).ToArray();
            permissions = db.Set<TRolePermission>().AsNoTracking().Where(a => roleIds.Contains(a.RoleId)).Select(a => a.Permission).ToArray();
        }
        else
        {
            permissions = [];
        }
        UserCacheInfo cache = new(key, user.Username, user.Name, roleCodes, permissions, user.OrganizationId);
        return cache;
    }
}
